package com.fineaiops.gateway.service;

import com.fineaiops.gateway.bean.LogEntity;
import com.fineaiops.gateway.bean.LogExpEntity;
import com.fineaiops.gateway.util.ConstString;
import com.fineaiops.gateway.util.JsonBuilder;
import com.fineaiops.gateway.util.LCSUtil;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class ExpService {
    public RestHighLevelClient client;

    public ExpService(@NotNull EsClient client) {
        this.client = client.getClient();
    }

    public static void main(String[] args) {
        try {
            //System.out.println(Arrays.toString(getExceptionCategories(new String[]{"ZnJvbnRlbmQ=.1"}, new long[]{}, new long[]{})));
        } catch (Exception e) {
            System.out.println(e);
        }
    }
    public SearchHit[] getExceptionCategories (
            String[] serviceID,
            long[] levels,
            long[] status,
            int offset,
            int limit
    ) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ConstString.exceptionIndex);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (serviceID.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("service_id.keyword",serviceID));
        }
        if (status.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("status",status));
        }
        if (levels.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("level",levels));
        }
        sourceBuilder.sort(new FieldSortBuilder("last_report_time").order(SortOrder.DESC))
                .from(offset * limit)
                .size(limit)
                .query(boolQueryBuilder);

        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(searchResponse.getHits().getTotalHits().value);
        return searchResponse.getHits().getHits();
    }

    public GetResponse getExceptionCateByID(String id) throws IOException {
        GetRequest getRequest = new GetRequest(ConstString.exceptionIndex, id);
        return client.get(getRequest, RequestOptions.DEFAULT);
    }

    public SearchHit[] getExceptionCategories (
            String[] serviceID,
            long[] levels,
            long[] status
    ) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ConstString.exceptionIndex);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (serviceID.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("service_id.keyword",serviceID));
        }
        if (status.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("status",status));
        }
        if (levels.length > 0) {
            boolQueryBuilder.must(QueryBuilders.termsQuery("level",levels));
        }
        sourceBuilder.sort(new FieldSortBuilder("last_report_time").order(SortOrder.DESC))
                .query(boolQueryBuilder).size(1000);

        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        System.out.println(searchResponse.getHits().getTotalHits().value);
        return searchResponse.getHits().getHits();
    }

    public GetResponse getLogByID(String id) throws IOException {
        GetRequest getRequest = new GetRequest(ConstString.logIndex, id);
        return client.get(getRequest, RequestOptions.DEFAULT);
    }

    public List<LogEntity> getLogByCategory(String categoryID, int offset, int limit, SortOrder order) throws IOException{
        SearchRequest request = new SearchRequest(ConstString.logIndex)
                .source(new SearchSourceBuilder()
                        .query(QueryBuilders.termsQuery("type_id.keyword", categoryID))
                        .sort(new FieldSortBuilder("timestamp").order(order))
                        .from(offset * limit)
                        .size(limit));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        List<LogEntity> logs = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            Map<String, Object> map = hit.getSourceAsMap();
            logs.add(new LogEntity(hit.getId(), (Long)map.get("timestamp"), (String)map.get("content"), categoryID));
        }
        return logs;
    }

    public int getLogSum(String categoryID) throws IOException{
        SearchRequest request = new SearchRequest(ConstString.logIndex)
                .source(new SearchSourceBuilder()
                        .query(QueryBuilders.termsQuery("type_id.keyword", categoryID))
                        .size(1000));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        return response.getHits().getHits().length;

    }

    public SearchHit[] getRelativeExceptionByContent(String content, ArrayList<String> serviceID) throws IOException{
        SearchRequest request = new SearchRequest(ConstString.exceptionIndex)
                .source(new SearchSourceBuilder()
                        .query(QueryBuilders.boolQuery()
                                .must(QueryBuilders.termsQuery("service_id.keyword", serviceID))
                                .must(QueryBuilders.matchQuery("content", content))
        ));
        SearchResponse response= client.search(request,RequestOptions.DEFAULT);
        return response.getHits().getHits();
    }

    public List<LogExpEntity> getAllLogCate() throws IOException {
        SearchRequest request = new SearchRequest(ConstString.exceptionIndex)
                .source(new SearchSourceBuilder()
                        .query(QueryBuilders.matchAllQuery()).size(5000));
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        List<LogExpEntity> res = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            res.add(new LogExpEntity(hit.getId(),(String)hit.getSourceAsMap().get("service_id")));
        }
        return res;
    }

    public void updateLogExpScore(String id, Double score) throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("score", score);
        UpdateRequest request = new UpdateRequest(ConstString.exceptionIndex, id).doc(map);
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    }

    public void updateLogExpLevel(String id, int level) throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("level", level);
        UpdateRequest request = new UpdateRequest(ConstString.exceptionIndex, id).doc(map);
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    }

    public String getMatchCategories(String msg, String service, String logger) throws IOException {
        SearchRequest request = new SearchRequest(ConstString.exceptionIndex)
                .source(new SearchSourceBuilder()
                        .size(1)
                        .query(QueryBuilders.boolQuery()
//                                .must(QueryBuilders.termsQuery("log_level.keyword", level))
                                .must(QueryBuilders.termsQuery("service_id.keyword", service))
                                .must(QueryBuilders.termsQuery("logger.keyword", logger))
                                .must(QueryBuilders.matchQuery("log_message", msg))
                        ));
        SearchResponse response= client.search(request,RequestOptions.DEFAULT);
        if (response == null || response.getHits() == null || response.getHits().getHits() == null ||
                response.getHits().getHits().length == 0) {
            System.out.println("sk-means null ");
            return null;
        }
        SearchHit cate = response.getHits().getHits()[0];
        Map<String, Object> map = cate.getSourceAsMap();
        System.out.println("sk-means score: "+cate.getScore());
//        if(cate.getScore() / (Float) map.get("msg_score") < 80) {
        if(cate.getScore() / (float) msg.split(" ").length < 1.0) {
            System.out.println("sk-means null: "+cate.getScore() / (float) msg.split(" ").length);
            return null;
        }
        return cate.getId();
    }

    public String createCategory(
            long time,
//            String level,
            String msg,
            String service,
            String logger
    ) throws Exception {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date(time);
        String timeStr = simpleDateFormat.format(date);

        String entity = new JsonBuilder()
                .put("first_report_time", timeStr)
                .put("last_report_time", timeStr)
//                .put("log_level", level)
                .put("log_message", msg)
                .put("content", msg)
                .put("service_id", service)
                .put("logger", logger)
                .put("status",0)
                .build().toJSONString();
        IndexRequest request = new IndexRequest(ConstString.exceptionIndex).source(entity,XContentType.JSON);
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        return response.getId();
    }

    public String createLog(
            String id,
            long time,
//            String level,
            String msg,
            String logger,
            String cateID
    ) throws Exception{
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date(time);
        String timeStr = simpleDateFormat.format(date);

//        String content = timeStr + " " + level + " " + logger + " : " + msg;
        String content = timeStr + " " + logger + " : " + msg;
        String entity = new JsonBuilder().put("content", content)
                .put("timestamp", time)
                .put("type_id", cateID)
                .build().toJSONString();
        IndexRequest request = new IndexRequest(ConstString.logIndex, "_doc", id).source(entity, XContentType.JSON);
        IndexResponse response = client.index(request, RequestOptions.DEFAULT);
        return response.getId();
    }

    public void addLogIDtoCategory(String cateID, String logID, String msg, long time) throws Exception {
        GetResponse cateRes = getExceptionCateByID(cateID);
        Map<String, Object> map = cateRes.getSourceAsMap();
        UpdateRequest request = new UpdateRequest(ConstString.exceptionIndex, cateID);
        if(map.get("log_ids") == null) {
            request.doc(new JsonBuilder().put("log_ids", new String[]{logID})
                    .build().toJSONString(), XContentType.JSON);
            client.update(request, RequestOptions.DEFAULT);
            return;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date(time);
        String timeStr = simpleDateFormat.format(date);
        String content = (String)map.get("content");
        content = LCSUtil.LCS(content.split(" "), msg.split(" "));
        request.script(new Script("ctx._source.log_ids.add('"+logID+"'); " +
                        "ctx._source.last_report_time='"+timeStr+"';"
//                        +"ctx._source.content='"+content+"'"
                ));
        UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
    }


}
